import java.io.*;
import java.net.*;
import java.security.*;
import java.util.*;
import javax.net.*;
import javax.net.ssl.*;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class EM_Client implements Runnable
{
  private DataInputStream din;
  private DataOutputStream dout;

  private KeyStore emKeyStore;         //La nuestra
  private KeyStore mfKeyStore;         //La del MF

  private SSLContext sslContext;
  
  static private final String passphrase = "empass";

  static private SecureRandom secureRandom;

  //TODO: Poner estas constraints en la BD.
  private final int MAX_EXPERIMENTS_USUARI = 3;
  private final int MAX_EXPERIMENTS_EMPRESA = 10;
  
  private static String BDuser = "expManager";
  private static String BDpassword = "pxCloud2011";

  public EM_Client ( String host, int port ) {
       
    connect( host, port );

    new Thread( this ).start();
  }

  private void setupServerKeystore() throws GeneralSecurityException, IOException {
    mfKeyStore = KeyStore.getInstance( "JKS" );
    mfKeyStore.load( new FileInputStream( "mf.public" ), 
                        "public".toCharArray() );
  }

  private void setupClientKeyStore() throws GeneralSecurityException, IOException {
    emKeyStore = KeyStore.getInstance( "JKS" );
    emKeyStore.load( new FileInputStream( "em.private" ),
                       passphrase.toCharArray() );
  }

  private void setupSSLContext() throws GeneralSecurityException, IOException {
    TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
    tmf.init( mfKeyStore );

    KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
    kmf.init( emKeyStore, passphrase.toCharArray() );

    sslContext = SSLContext.getInstance( "TLS" );
    sslContext.init( kmf.getKeyManagers(),
                     tmf.getTrustManagers(),
                     secureRandom );
  }

  private void connect( String host, int port ) {
    try {
      secureRandom = new SecureRandom(); 
      secureRandom.nextInt();
      
      setupServerKeystore();
      setupClientKeyStore();
      setupSSLContext();

      SSLSocketFactory sf = sslContext.getSocketFactory();
      SSLSocket socket = (SSLSocket)sf.createSocket( host, port ); 

      InputStream in = socket.getInputStream();
      OutputStream out = socket.getOutputStream();

      din = new DataInputStream( in );
      dout = new DataOutputStream( out );
    } catch( GeneralSecurityException gse ) {
      gse.printStackTrace();
    } catch( IOException ie ) {
      ie.printStackTrace();
    }
  }

  public void run() {
    String usuari;
    String empresa;
    GestorBD gestorbd = new GestorBD();

    
    try{
      //Obtenemos los experimentos pendientes
      Class.forName("com.mysql.jdbc.Driver");
      Connection connect = DriverManager.getConnection("jdbc:mysql://localhost/em?"+"user="+BDuser+"&password="+BDpassword);
      Statement statement_exp = connect.createStatement();
      Statement statement_aux = connect.createStatement();
      
      ResultSet experiments = statement_exp.executeQuery("select * from em.EXPERIMENTS WHERE STATUS = 'Pendent';");
      ResultSet count_usuari=null;
      ResultSet count_empresa=null;
      
      //Miramos si podemos lanzar alguno (trobat==true):
      //TODO:Refinar el bucle (no hacer más de un acceso a BD por usuario). Igual su puede hacer con SQL desde la executeQuery anterior...
      boolean trobat = false;
      while ( !trobat && experiments.next()){
	usuari = experiments.getString("USUARI");
	empresa = experiments.getString("EMPRESA");
	
	count_usuari = statement_aux.executeQuery("SELECT COUNT(id) FROM em.EXPERIMENTS WHERE (USUARI='"+usuari+"'AND STATUS='Calculant');");
	
	count_usuari.next();
	int aux = count_usuari.getInt("COUNT(id)");
	
	if ( aux <= MAX_EXPERIMENTS_USUARI){
	  
	  count_empresa = statement_aux.executeQuery("SELECT COUNT(id) FROM em.EXPERIMENTS WHERE (EMPRESA='"+empresa+"'AND STATUS='Calculant');");
	  
	  count_empresa.next();
	  aux = count_empresa.getInt("COUNT(id)");
	  
	  if ( aux <= MAX_EXPERIMENTS_EMPRESA)
	    trobat = true;
     
	}	
      
      }

      if (trobat){

	//Escribimos el Id del experimento (único para el Experiment Manager):
	int id = experiments.getInt("id");
	String idExperiment = experiments.getString("IDEXPERIMENT");
	usuari = experiments.getString("USUARI");
	empresa = experiments.getString("EMPRESA");
	dout.writeInt( id );
	
	BufferedReader bin = new BufferedReader(new FileReader(idExperiment+usuari+empresa+".dat"));
	System.out.println("Launching experiment "+id+": "+idExperiment+usuari+empresa);
	String dades;
	while ((dades = bin.readLine()) != null){
	  dout.writeUTF(dades);
	
	}
	dout.writeUTF( "Fin!" );
	
	gestorbd.modificar("em", BDuser, BDpassword,
		"UPDATE em.EXPERIMENTS SET STATUS = 'Calculant' WHERE id="+id+";");
      }
      //Cerramos la conexión con la BBDD
      if (experiments!=null)
	experiments.close();
      if (count_usuari!=null)
	count_usuari.close();
      if (count_empresa!=null)
	count_empresa.close();
      if (statement_exp!=null)
	statement_exp.close();
      if (statement_aux!=null)
	statement_aux.close();
      if (connect!=null)
	connect.close();
    
    }
    catch (Exception e){e.printStackTrace();}
  }

}
